import * as d3 from 'd3';
import SequenceUtils from './sequence-utils';
import sequenceTheme from './sequence-theme';

const defaultOptions = {
  selector: '#diagram',
  padding: { top: 80, right: 20, bottom: 50, left: 20 },
  theme: 'dark',
  LAYOUT: {
    X_OFFSET: 50,
    X_BOX_WIDTH: 135,
    X_BOX_HIGH: 160,
    X_BOX_SPACING: 60,
    Y_LINE_SPACING: 60,
    SHORT_LINE_LEN: 30,
    SHORT_LABEL_WIDTH: 170,
    SHORT_LABEL_HIGH: 40,
  },
  onDropdownClick: () => void 0,
};

export default class Sequence {
  constructor(data, options={}) {
    this.options = Object.assign(defaultOptions, options);
    this.utils = new SequenceUtils(this.options);
    this.theme = sequenceTheme(this.options.theme);
    this.initData(data)
    this.initContainer();
    this.initEvent();
  }

  initData({ xData, yData }) {
    this.xDataList = xData;
    this.xDataIdList = this.xDataList.map(t => t.id);
    this.yDataList = yData
  }

  initEvent() {
    this.container.selectAll('.dropdown-triangle')
      .on('click', (d) => {
        this.options.onDropdownClick(d)
      })
  }

  initContainer() {
    const { selector, padding, LAYOUT } = this.options;
    const { X_BOX_WIDTH, X_BOX_SPACING, Y_LINE_SPACING, X_BOX_HIGH } = LAYOUT;
    // 创建一个分组用来组合要画的图表元素
    const containerWidth =
      (X_BOX_WIDTH + X_BOX_SPACING) * this.xDataList.length +
      X_BOX_SPACING +
      padding.left +
      padding.right;
    const containerHigh =
      Y_LINE_SPACING * (this.yDataList.length + 1) +
      X_BOX_HIGH +
      padding.top +
      padding.bottom;

    this.container = d3
      .select(selector)
      .attr('transform', `translate(${padding.top}, ${padding.left})`)
      .attr('width', containerWidth)
      .attr('height', containerHigh);
    this.initCoordinates();
    this.drawArrow();
    this.drawDropDonwMenuIcon(X_BOX_WIDTH)
  }

  initCoordinates() {
    const { LAYOUT } = this.options
    this.xDataList.forEach((xData, i) => {
      this.drawYAxisLine(i, LAYOUT);
      this.drawXAxisRect(i, LAYOUT, xData);
    });
    this.yDataList.forEach((yData, i) => {
      if (yData.target && yData.source) {
        this.drawLangArrowLine(yData, i, LAYOUT);
        this.drawLangLabel(yData, i, LAYOUT);
      } else if (yData.target || yData.source){
        this.drawShortArrowLine(yData, i, LAYOUT);
      }
    });
  }

  drawYAxisLine(
    i,
    { X_OFFSET, X_BOX_WIDTH, X_BOX_HIGH, X_BOX_SPACING, Y_LINE_SPACING },
  ) {
    this.container
      .append('line')
      .style('stroke', this.theme.dashColor)
      .style('stroke-dasharray', '12')
      .attr('x1', X_OFFSET + X_BOX_SPACING + i * (X_BOX_WIDTH + X_BOX_SPACING))
      .attr('y1', X_BOX_HIGH + this.options.padding.top)
      .attr('x2', X_OFFSET + X_BOX_SPACING + i * (X_BOX_WIDTH + X_BOX_SPACING))
      .attr(
        'y2',
        X_BOX_HIGH +
          this.options.padding.top +
          (this.yDataList.length + 1) * Y_LINE_SPACING,
      );
  }

  drawXAxisRect(
    i,
    { X_OFFSET, X_BOX_WIDTH, X_BOX_HIGH, X_BOX_SPACING },
    xData,
  ) {
    const g = this.container
      .append('g')
      .attr(
        'transform',
        `translate(${X_BOX_SPACING +
          i * (X_BOX_WIDTH + X_BOX_SPACING)}, ${X_BOX_HIGH})`,
      )
      .attr('class', 'account-panel');

    g.append('rect')
      .attr('x', -X_BOX_WIDTH / 2 + X_OFFSET)
      .attr('y', -X_BOX_HIGH + this.options.padding.top)
      .attr('rx', 10)
      .attr('ry', 10)
      .attr('width', X_BOX_WIDTH)
      .attr('height', X_BOX_HIGH)
      .attr('fill', 'rgba(0,0,0,0)')
      .attr('stroke', this.theme.rectColor)
      .attr('stroke-width', 1);

    g.append('foreignObject')
      .attr('width', X_BOX_WIDTH - 26)
      .attr('height', X_BOX_HIGH - 50)
      .attr('x', -18)
      .attr('y', -75)
      .append('xhtml:div')
      .style('color', this.theme.rectColor)
      .style('font-size', '20px')
      .style('padding', '15px 14px')
      .style('height', '80px')
      .text(xData.label);

    g.append('text')
      .text(() => xData.notice)
      .style('font-size', '14px')
      .attr('fill', this.theme.rectColor)
      .attr('text-anchor', 'middle')
      .attr('dy', 50)
      .attr('dx', 10);
  }

  drawDropDonwMenuIcon(X_BOX_WIDTH) {
    const triangle = d3
      .symbol()
      .type(d3.symbolTriangle)
      .size(50);

    this.container.selectAll('.account-panel')
      .data(this.xDataList)
      .append('path')
      .attr('d', triangle)
      .attr('transform', `translate(${X_BOX_WIDTH - 40}, -45) rotate(180)`)
      .attr('class', 'dropdown-triangle')
      .style('fill', this.theme.rectColor)
      .style('cursor', 'pointer')
  }

  drawLangArrowLine(yData, i, { Y_LINE_SPACING, X_BOX_HIGH }) {
    this.container
      .append('line')
      .style('stroke', this.theme.arrowColor)
      .style('stroke-width', '3')
      .attr(
        'x1',
        this.utils.getline_xAxis(this.xDataIdList.indexOf(yData.source)),
      )
      .attr(
        'y1',
        X_BOX_HIGH + this.options.padding.top + Y_LINE_SPACING * (i + 1),
      )
      .attr(
        'x2',
        this.utils.getline_xAxis(this.xDataIdList.indexOf(yData.target)),
      )
      .attr(
        'y2',
        X_BOX_HIGH + this.options.padding.top + Y_LINE_SPACING * (i + 1),
      )
      .attr('marker-end', 'url(#end)');
  }

  drawLangLabel(
    yData,
    i,
    { X_OFFSET, X_BOX_WIDTH, X_BOX_HIGH, Y_LINE_SPACING },
  ) {
    const baseUnitX = X_OFFSET + X_BOX_WIDTH + 20;
    const baseUnitY = X_BOX_HIGH + this.options.padding.top;
    const targetIndex = this.xDataIdList.indexOf(yData.target);
    const sourceIndex = this.xDataIdList.indexOf(yData.source);
    const offset = sourceIndex < targetIndex ? -1 : 1;
    const toRight = sourceIndex < targetIndex;
    let xAxis = this.utils.getline_xAxis(targetIndex) + 20;
    if (toRight) {
      xAxis = xAxis - baseUnitX;
    }
    const yAxis = baseUnitY + (i + 1) * Y_LINE_SPACING;
    const g = this.container
      .append('g')
      .attr('transform', `translate(${xAxis}, ${yAxis})`);

    g.append('text')
      .attr('dy', '-8px')
      .attr('text-anchor', 'begin')
      .attr('word-spacing', '1.2')
      .attr('font-size', '14px')
      .attr('fill', this.theme.labelColor)
      .text(() => this.utils.formatTimestamps(yData.value.time));

    g.append('text')
      .attr('dy', '18px')
      .attr('text-anchor', 'begin')
      .attr('font-size', '14px')
      .attr('fill', this.theme.labelColor)
      .text(() => `￥${yData.value.amount} 万元`);
  }

  drawShortArrowLine(yData, i, LAYOUT) {
    const { SHORT_LINE_LEN, Y_LINE_SPACING, X_BOX_HIGH } = LAYOUT
    const { source, target } = yData
    const flowOut = source && !target
    let x1, x2
    if (flowOut) {
      // 流出
      x1 = this.utils.getline_xAxis(this.xDataIdList.indexOf(source))
      x2 = x1 - SHORT_LINE_LEN
    } else {
      // 流入
      x2 = this.utils.getline_xAxis(this.xDataIdList.indexOf(target))
      x1 = x2 - SHORT_LINE_LEN
    }
    const y = X_BOX_HIGH + this.options.padding.top + Y_LINE_SPACING * (i + 1)
    this.container
      .append('line')
      .style('stroke', this.theme.arrowColor)
      .style('stroke-width', '3')
      .attr('x1', x1)
      .attr('x2', x2)
      .attr('y1', y)
      .attr('y2', y)
      .attr('marker-end', 'url(#end)');
    
    this.drawShortLabel(flowOut ? x2 : x1, y, yData, LAYOUT)
  }

  drawShortLabel(xlineLeft, y, yData, { SHORT_LABEL_WIDTH, SHORT_LABEL_HIGH }) {
    const SHORT_MARGIN_LEFT = 0
    const xAxis = xlineLeft - SHORT_LABEL_WIDTH - SHORT_MARGIN_LEFT
    // const label_x2 = xlineLeft - SHORT_LINE_LEN
    const yAxis = y - SHORT_LABEL_HIGH / 2
    const div = this.container
      .append('g')
      .attr('transform', `translate(${xAxis}, ${yAxis})`)
      .append('foreignObject')
      .attr('width', SHORT_LABEL_WIDTH)
      .attr('height', SHORT_LABEL_HIGH)
      .attr('x', 0)
      .attr('y', 0)
      .append('xhtml:div')
      .style('color', this.theme.rectColor)
      .style('font-size', '14px')
      .style('padding-left', '16px')
    div.append('xhtml:div')
      .style('height', '20px')
      .text(this.utils.formatTimestamps(yData.value.time))
    div.append('xhtml:div')
      .style('height', '20px')
      .text(`￥${yData.value.amount}万元`)
  }

  drawArrow() {
    this.container
      .append('svg:defs')
      .selectAll('marker')
      .data(['end'])
      .enter()
      .append('svg:marker')
      .attr('id', String)
      .attr('viewBox', '0 -5 10 10')
      .attr('refX', 4)
      .attr('refY', 0)
      .attr('markerWidth', 10)
      .attr('markerHeight', 10)
      .attr('orient', 'auto')
      .append('svg:path')
      .attr('d', 'M0,-5L10,0L0,5')
      .attr('transform', 'scale(0.5)')
      .style('fill', this.theme.arrowColor);
  }
}
